package util

import (
	"reflect"
	"strings"
)

/*
 * 	tip1: []interface{} use Filter []interface{}
		productAttributeValuesOfTheKey := util.ToStringSlice(util.Filter(util.ToGenericSlice(productAttributeValues), func(v interface{}) bool {
			pav := v.(sqlc.ProductAttributeValue)
			return pav.ProductAttributeKeyID == productAttributeKey.ID
		}))

 * 	tip2: []type{} use Filter []interface{}
		productAttributeValuesOfTheKey := util.ToStringSlice(util.Map(util.ToGenericSlice(productAttributeValues), func(v interface{}) interface{} {
			pav := v.(sqlc.ProductAttributeValue)
			return pav.ProductAttributeKeyID
		}))

		tip3 []type{} use MapToInt64 []int64
		productAttributeKeyIDs := util.MapToInt64(util.ToGenericSlice(productAttributeKeys), func(v interface{}) int64 {
		productAttributeKey := v.(sqlc.ProductAttributeKey)
		return productAttributeKey.ID
		})
*/

// StringFind find string in string[]
func StringFind(list []string, val string) (int, bool) {
	for i, item := range list {
		if item == val {
			return i, true
		}
	}
	return -1, false
}

// StringContains check if val in string slice
func StringContains(list []string, val string) bool {
	_, found := StringFind(list, val)
	return found
}

// StringFilter filter string slice of predictor
func StringFilter(list []string, f func(string) bool) (r []string) {
	for _, it := range list {
		if f(it) {
			r = append(r, it)
		}
	}
	return r
}

// Int64Find find int64 in int64[]
func Int64Find(list []int64, val int64) (int, bool) {
	for i, item := range list {
		if item == val {
			return i, true
		}
	}
	return -1, false
}

// Int64Contains check if val in int64 slice
func Int64Contains(list []int64, val int64) bool {
	_, found := Int64Find(list, val)
	return found
}

// Int64Filter filter int64 slice of predictor
func Int64Filter(list []int64, f func(int64) bool) (r []int64) {
	for _, it := range list {
		if f(it) {
			r = append(r, it)
		}
	}
	return r
}

// UInt32Find find uint32 in uint32[]
func UInt32Find(list []uint32, val uint32) (int, bool) {
	for i, item := range list {
		if item == val {
			return i, true
		}
	}
	return -1, false
}

// UInt32Contains check if val in uint32 slice
func UInt32Contains(list []uint32, val uint32) bool {
	_, found := UInt32Find(list, val)
	return found
}

// UInt32Filter filter uint32 slice of predictor
func UInt32Filter(list []uint32, f func(uint32) bool) (r []uint32) {
	for _, it := range list {
		if f(it) {
			r = append(r, it)
		}
	}
	return r
}

// Filter filter []interface{} => []interface{}
func Filter(list []interface{}, f func(interface{}) bool) (r []interface{}) {
	for _, it := range list {
		if f(it) {
			r = append(r, it)
		}
	}
	return r
}

// ToGenericSlice []any => []interface{}
func ToGenericSlice(arr interface{}) []interface{} {
	s := reflect.ValueOf(arr)
	v := make([]interface{}, s.Len())
	for i := 0; i < s.Len(); i++ {
		// fmt.Println(s.Index(i))
		v[i] = s.Index(i).Interface()
	}

	return v
}

// ToStringSlice []interface{} -> []string
func ToStringSlice(arr []interface{}) []string {
	v := make([]string, len(arr))
	for idx, it := range arr {
		v[idx] = it.(string)
	}
	return v
}

// ToInt64Slice []interface{} -> []int64
func ToInt64Slice(arr []interface{}) []int64 {
	v := make([]int64, len(arr))
	for idx, it := range arr {
		v[idx] = it.(int64)
	}
	return v
}

// ToInt32Slice []interface{} -> []int32
func ToInt32Slice(arr []interface{}) []int32 {
	v := make([]int32, len(arr))
	for idx, it := range arr {
		v[idx] = it.(int32)
	}
	return v
}

// ToUInt32Slice []interface{} -> []uint32
func ToUInt32Slice(arr []interface{}) []uint32 {
	v := make([]uint32, len(arr))
	for idx, it := range arr {
		v[idx] = it.(uint32)
	}
	return v
}

// MapToInt64 map int64 slice of converter
func MapToInt64(list []interface{}, f func(interface{}) int64) (r []int64) {
	for _, it := range list {
		r = append(r, f(it))
	}
	return r
}

// MapToInt32 map int32 slice of converter
func MapToInt32(list []interface{}, f func(interface{}) int32) (r []int32) {
	for _, it := range list {
		r = append(r, f(it))
	}
	return r
}

// MapFromInt32ToUInt32 map []int32 -> []uint32
func MapFromInt32ToUInt32(list []int32) (r []uint32) {
	for _, it := range list {
		r = append(r, uint32(it))
	}
	return r
}

// MapFromUInt32ToInt32 map []uint32 -> []int32
func MapFromUInt32ToInt32(list []uint32) (r []int32) {
	for _, it := range list {
		r = append(r, int32(it))
	}
	return r
}

// MapFromInt64ToUInt64 map []int64 -> []uint64
func MapFromInt64ToUInt64(list []int64) (r []uint64) {
	for _, it := range list {
		r = append(r, uint64(it))
	}
	return r
}

// MapFromUInt64ToInt64 map []uint64 -> []int64
func MapFromUInt64ToInt64(list []uint64) (r []int64) {
	for _, it := range list {
		r = append(r, int64(it))
	}
	return r
}

// MapToString map string slice of converter
func MapToString(list []interface{}, f func(interface{}) string) (r []string) {
	for _, it := range list {
		r = append(r, f(it))
	}
	return r
}

// Map map slice of converter
func Map(list []interface{}, f func(interface{}) interface{}) (r []interface{}) {
	for _, it := range list {
		r = append(r, f(it))
	}
	return r
}

// Each filter []interface{} => []interface{}
func Each(list []interface{}, f func(interface{})) {
	for _, it := range list {
		f(it)
	}
}

// Any any elem match predicator []interface{} => bool
func Any(list []interface{}, f func(interface{}) bool) (r bool) {
	for _, it := range list {
		r = f(it)
		if r {
			break
		}
	}
	return
}

// Any any string elem match predicator []string => bool
func AnyString(list []string, f func(string) bool) (r bool) {
	for _, it := range list {
		r = f(it)
		if r {
			break
		}
	}
	return
}

// All all elem match predicator []interface{} => bool
func All(list []interface{}, f func(interface{}) bool) (r bool) {
	for _, it := range list {
		r = f(it)
		if !r {
			break
		}
	}
	return
}

// All all string elem match predicator []string => bool
func AllString(list []string, f func(string) bool) (r bool) {
	for _, it := range list {
		r = f(it)
		if !r {
			break
		}
	}
	return
}

// Ord 排序值
type Ord int8

const (
	// Lt less than
	Lt Ord = -1
	// Eq equal
	Eq Ord = 0
	// Gt great than
	Gt Ord = 1
)

// MaxInt find max int from []int
func MaxInt(list []int) (max int) {
	max = list[0]
	for _, it := range list[1:] {
		if MaxIntComparer(max, it) == Lt {
			max = it
		}
	}
	return
}

// MaxInt find max int32 from []int32
func MaxInt32(list []int32) (max int32) {
	max = list[0]
	for _, it := range list[1:] {
		if MaxInt32Comparer(max, it) == Lt {
			max = it
		}
	}
	return
}

func MaxIntComparer(a, b int) Ord {
	if a-b < 0 {
		return Lt
	} else if a-b > 0 {
		return Gt
	}
	return Eq
}

func MaxInt32Comparer(a, b int32) Ord {
	return MaxIntComparer(int(a), int(b))
}

func MaxUInt32Comparer(a, b uint32) Ord {
	return MaxIntComparer(int(a), int(b))
}

// MaxUInt32 find max uint32 from []uint32
func MaxUInt32(list []uint32) (max uint32) {
	max = list[0]
	for _, it := range list[1:] {
		if MaxUInt32Comparer(max, it) == Lt {
			max = it
		}
	}
	return
}

// Max return max item by tester f
func Max(list []interface{}, f func(interface{}, interface{}) Ord) (max interface{}) {
	max = list[0]
	for _, it := range list[1:] {
		if f(max, it) == Lt {
			max = it
		}
	}
	return
}

// Min return min item by tester f
func Min(list []interface{}, f func(interface{}, interface{}) Ord) (min interface{}) {
	min = list[0]
	for _, it := range list[1:] {
		if f(min, it) == Gt {
			min = it
		}
	}
	return
}

// MaxWithMin return max and min item by tester f
func MaxWithMin(list []interface{}, f func(interface{}, interface{}) Ord) (max interface{}, min interface{}) {
	max = list[0]
	min = list[0]
	for _, it := range list[1:] {
		if f(max, it) == Lt {
			max = it
		}
		if f(min, it) == Gt {
			min = it
		}

	}
	return
}

type ReducedObject struct {
	G interface{}
	A interface{}
}
type ReducedObjectStringMap map[string]*ReducedObject

// MapReduceString []interface => []interface{}
func MapReduceString(list []interface{},
	keyFunc func(interface{}) string,
	groupFunc func(interface{}) interface{},
	aggFunc func(interface{}, interface{}) interface{},
	mergeFunc func(*ReducedObject) interface{},
	initFunc func () interface{}) (r []interface{}) {
	ro := make(ReducedObjectStringMap)
	for _, it := range list {
		k := keyFunc(it)
		if _, ok := ro[k]; !ok {
			g := groupFunc(it)
			ro[k] = &ReducedObject{
				G: g,
				A: aggFunc(initFunc(), it),
			}
		} else {
			ro[k].A = aggFunc(ro[k].A, it)
		}
	}
	for k := range ro {
		rov := ro[k]
		r = append(r, mergeFunc(rov))
	}
	return
}

type GroupObjectInt64Map map[int64][]interface{}

// []interface => map[int64][]interface{}
func ListToInt64Map(list []interface{},
	keyFunc func(interface{}) int64,
) (v GroupObjectInt64Map) {
	v = make(GroupObjectInt64Map)
	for _, it := range list {
		k := keyFunc(it)
		if _, ok := v[k]; !ok {
			v[k] = []interface{}{}
		}
		v[k] = append(v[k], it)
	}
	return
}

// FoldLeft fold slice from left to right
func FoldLeft(list []interface{}, f func(interface{}, interface{}) interface{}, init interface{}) (ret interface{}) {
	ret = init
	for _, it := range list {
		ret = f(ret, it)
	}
	return
}

// FoldLeftInt fold slice int64 from left to right
func FoldLeftInt(list []int, f func(int, int) int, init int) (ret int) {
	ret = init
	for _, it := range list {
		ret = f(ret, it)
	}
	return
}

// FoldLeftInt64 fold slice int64 from left to right
func FoldLeftInt64(list []int64, f func(int64, int64) int64, init int64) (ret int64) {
	ret = init
	for _, it := range list {
		ret = f(ret, it)
	}
	return
}

// FoldLeftInt32 fold slice int32 from left to right
func FoldLeftInt32(list []int32, f func(int32, int32) int32, init int32) (ret int32) {
	ret = init
	for _, it := range list {
		ret = f(ret, it)
	}
	return
}

// FoldLeftString fold slice stringslice from left to right
func FoldLeftStringSlice(list []interface{}, f func([]string, interface{}) []string, init []string) (ret []string) {
	ret = init
	for _, it := range list {
		ret = f(ret, it)
	}
	return
}

// SliceToMapString return map[string]interface from slice []interface{}
func SliceToMapString(list []interface{}, f func(map[string]interface{}, interface{})) map[string]interface{} {
	theMap := map[string]interface{}{}
	for _, it := range list {
		f(theMap, it)
	}
	return theMap
}

// SliceToMapInt64 return map[int64]interface from slice []interface{}
func SliceToMapInt64(list []interface{}, f func(map[int64]interface{}, interface{})) map[int64]interface{} {
	theMap := map[int64]interface{}{}
	for _, it := range list {
		f(theMap, it)
	}
	return theMap
}

func MinTwoInt(a, b int) int {
	return IntIfThenElse(a-b >= 0, b, a)
}

func LeftTo(list []interface{}, i int) []interface{} {
	idx := MinTwoInt(len(list), i)
	return list[0:idx]
}

func RightTo(list []interface{}, i int) []interface{} {
	idx := MinTwoInt(len(list), i)
	return list[idx:]
}

func LeftToString(list []string, i int) []string {
	idx := MinTwoInt(len(list), i)
	return list[0:idx]
}

func RightToString(list []string, i int) []string {
	idx := MinTwoInt(len(list), i)
	return list[0:idx]
}

func JoinString(list []string, sep string) string {
	return strings.Join(list, sep)
}
